網站上線了,也擋住了幾隻鯊魚的攻擊。可是還是有幾個問題,我到現在還沒辦法知道:
別人在 Google 搜尋得到我的網站嗎?打開來會不會很慢?來的人都在做什麼?如果某個使用者按了按鈕卻沒反應或是跑很久,我能知道嗎?
今天要做的,是讓網站被看見,也讓我能看得見網站的狀況。
網站上線後,我想知道四件事:能不能被找得到、用起來順不順、有多少人使用、有哪些地方會遇到錯誤,而每一件都有對應的工具:

【圖 1|四個面向與對應工具】
用餐廳對照:
Google 找到一個網頁,大致會經過三步:
| 步驟 | 做什麼 | 比喻 |
|---|---|---|
| 爬取 | 程式自動逛網站,沿著連結找到每一頁 | 圖書館員把新書搬進館 |
| 建立索引 | 讀懂每一頁在講什麼,存進資料庫 | 替每本書編目錄 |
| 排名 | 有人搜尋時,決定哪些頁面排前面 | 讀者問問題時,先推薦哪幾本 |
SEO(搜尋引擎最佳化)三步都有影響,但最基本的是前兩步:讓 Google 爬得到、看得懂。排名則由搜尋引擎綜合很多訊號決定,沒有人能保證。
| 項目 | 做什麼 | 比喻 |
|---|---|---|
| metadata | 每一頁的標題和描述,提供給搜尋引擎參考,搜尋結果可能會使用 | 書的書名和封底簡介 |
| Open Graph | 分享到 LINE、Facebook 時的預覽卡片 | 書店平台上的封面展示 |
| canonical | 同一個內容有多個網址時,指定哪一個是正本 | 註明「本書另有平裝版,以精裝版為準」 |
| sitemap.xml | 列出希望被搜尋引擎找到的重要網址 | 圖書館的館藏目錄 |
| robots.txt | 告訴守規矩的爬蟲,哪些路徑不用爬 | 「這區不用編目」的便條 |
| noindex | 寫在頁面裡,告訴搜尋引擎「這頁不要收錄」 | 書裡夾一張「本書不列入目錄」 |
| 結構化資料 | 用固定格式標註「這是一張專輯、歌手是誰、何時發行」 | 書的分類編號 |
robots.txt 和 noindex 管的是兩件事:robots.txt 管「要不要來爬」,noindex 管「要不要收錄」。想讓登入頁、後台不出現在搜尋結果,要讓爬蟲進來看到 noindex;如果在 robots.txt 擋掉,爬蟲反而讀不到那張「不要收錄」。
兩者都並非安全措施:robots.txt 是任何人都看得到的公開檔案,後台擋人靠的是 Day 21 的權限。
對這個專案來說,Open Graph 也很實用。K-pop 粉絲常把連結分享到群組,預覽卡片如果有專輯封面和名稱,別人一眼就知道這是什麼,可能比較願意點開。
跟之前一樣,我先請 Codex 做唯讀盤點,例如:
你是一位 SEO 與 AEO 專家,請分析這個專案目前的 SEO 與 AEO,
有哪些地方可以優化?請做深度分析,先不要修改。
結果有好有壞。專輯頁其實已經有動態的標題、描述和 canonical;但缺少 sitemap、robots、各頁面的收錄規則,品牌名稱也有「搜輯 Biashelf」「BIAS SHELF」混用的情況。
而這次分析的報告裡最有價值的,是一個從畫面上完全看不出來的問題。
一張專輯有好幾個版本,原本是利用點選畫面中的按鈕搭配參數切換。但盤點發現,伺服器回傳的 HTML 裡只有預設顯示的第一個版本的配置,其他版本會須要等瀏覽器執行 JavaScript、使用者點下去才會出現。
然而爬蟲並不會自己去點分頁。所以「Zine 版有幾張小卡」這種答案,明明在網站上,搜尋引擎卻看不到。
想讓爬蟲知道的內容,就要出現在伺服器回傳的 HTML 裡。 這也是為什麼內容全靠瀏覽器產生(CSR)的網站,SEO 效果有時比較差:Google 雖然能執行 JavaScript,但比較不穩定;LINE、Facebook 抓預覽時,主要讀取伺服器回傳的 HTML,不會像瀏覽器一樣執行互動。
修法是把所有版本都放進 HTML,沒選到的先隱藏起來,畫面不變。修好之後,補一條 E2E 測試直接讀原始 HTML,確認每個版本都在裡面。跟 Day 26 一樣,問題修好,就變成一條測試。
| 項目 | 做法 |
|---|---|
| sitemap | app/sitemap.ts 從資料庫讀出所有專輯,自動產生清單,附上每張專輯的最後更新時間 |
| robots | app/robots.ts 只排除 /api/,並寫上 sitemap 的位置 |
| noindex | 登入、帳號、收藏、後台設為不收錄;專輯列表只要帶查詢參數(搜尋、篩選、排序),也設為不收錄 |
| 預設 metadata | 統一的標題格式、預設分享圖、favicon;品牌名稱全站統一為「搜輯 Biashelf」 |
| 專輯分享圖 | 設計一個模板,依每張專輯的資料自動產生 |
| 版本內容 | 所有版本都放進伺服器 HTML(2.4) |
| 資料來源 | 專輯頁顯示最後核對日期與官方來源 |
| 結構化資料 | 標註專輯名稱、歌手、發行日期與麵包屑,只標頁面上看得到的資料 |
搜尋和篩選的網址組合幾乎無限多(?q=aespa、?year=2026&sort=new……),如果每一種都被收錄,搜尋引擎會把力氣花在大量重複的頁面上,所以只收錄不帶參數的列表頁。
分享圖可以不用每張專輯各做一張。 只要設計一個模板(左邊放封面,右邊放團名、專輯名稱和品牌),Next.js 會在有人分享時,依那張專輯的資料自動產生圖片:
// app/albums/[slug]/opengraph-image.tsx(簡化)
export default async function Image({ params }) {
const album = await getAlbum(params.slug);
return new ImageResponse(
<div style={{ display: "flex" }}>
<img src={album.coverUrl} />
<div>{album.title}・{album.artist}</div>
</div>,
{ width: 1200, height: 630, fonts: [/* 中文字型 */] }
);
}
兩個要注意的地方:中文字型要另外載入,否則中文會變成方塊,這點 AI 很常漏掉;另外,LINE 會記住預覽,同一個網址分享過一次,改了圖之後,舊的預覽可能還會留一陣子。

【圖 2|把專輯網址貼到 LINE 的預覽卡片】
sitemap 要一直重新提交嗎?
sitemap 幾種產生的作法:
- 手寫一個
sitemap.xml檔案(要自己更新)- 部署時用工具產生 (要重新部署才會更新)
- 有人讀取時由程式產生( 自動更新 )
不管哪一種,提交給 Search Console 都只需要一次,之後 Google 會自己定期回來讀。也有人把 sitemap 改名、只提交給 Google,不讓其他爬蟲知道;但公開的頁面,爬蟲沿著連結一樣找得到,藏起來反而讓 Bing 這類搜尋引擎少了一份清單。
爬蟲來的時候,資料庫會不會被打爆?
sitemap 每天最多重新產生一次,其他時候直接回傳做好的版本。專輯頁比較特別:root layout 會讀取登入狀態,這一行讓整個網站的每一頁都變成「每次請求才產生」。所以我改成讓頁面用到的資料,在 Vercel 的伺服器上快取一週,大多數訪客和爬蟲可以共用快取,不用每次都查資料庫。
快取拉長之後,資料改了不會馬上出現在網站上,所以我另外在後台加了一顆「更新專輯快取」按鈕:修正資料後按一下,網站就會拿到新資料。
SEO 之外:AEO
越來越多人直接問 ChatGPT 或看 Google 的 AI 摘要,而不是點進搜尋結果。AEO(Answer Engine Optimization)是讓這些 AI 更容易引用你的內容。沒有保證被引用的公式,但有些寫法比較容易被理解和引用:
- 答案要直接,不要藏在需要點擊的互動元件裡(2.4 正是這個問題)。
- 用表格整理,例如版本比較。
- 標示來源和日期,例如「最後核對:2026 年 10 月 6 日」。
Search Console 是 Google 提供的免費工具,可以看到 Google 怎麼看你的網站。sitemap 已經在 2.5 產生,部署到正式網站之後,這裡只要把網址交給 Google。
1. 驗證網域。 Google 會給一筆 TXT 記錄,加到 Cloudflare 的 DNS 裡。這就是 Day 23 提過的 TXT 記錄用途之一:證明「這個網域是我的」。

【圖 3|Google Search Console 驗證網域 - 1 新增資源】
(依序選擇新增網站 -> 輸入網域)

【圖 4|Google Search Console 驗證網域 - 2 開始驗證】

【圖 5|Google Search Console 驗證網域 - 3 Cloudflare 驗證】
(一段時間後會驗證成功)
2. 提交 sitemap。 先打開 你的sitemap網址,確認裡面的網址都是正式網域開頭,再到 Search Console 填入這個網址。

【圖 6|提交 sitemap】
3. 網址檢查。 一段時間後,在 Google Search Console 搜尋欄輸入任一頁網址,可以看到 Google 實際抓到了什麼、有沒有收錄。
收錄可能要幾天到數週以上,而且提交 sitemap 不保證每一頁都會被收錄。收錄的狀況可以在 Google Search Console 後台去查看。
Google 用三個指標衡量「使用者覺得快不快」,合稱 Core Web Vitals:
| 指標(完整名稱) | 量什麼 | Biashelf 的例子 | 良好標準 |
|---|---|---|---|
| LCP(Largest Contentful Paint,最大內容繪製) | 頁面主要內容多久出現 | 打開專輯頁,封面圖多久出現 | 2.5 秒內 |
| INP(Interaction to Next Paint,互動到下一次繪製) | 點擊或輸入後,畫面多久有反應 | 按下搜尋或收藏,畫面多久有變化 | 200 毫秒內 |
| CLS(Cumulative Layout Shift,累計版面位移) | 載入過程中,畫面會不會突然跳動 | 封面圖晚一步載入,把下面的按鈕往下推,結果點到別的東西 | 0.1 以下 |
這是效能最重要的觀念:
| 實驗室數據 | 真實數據 | |
|---|---|---|
| 怎麼來的 | 在一台電腦上模擬載入一次 | 收集真實使用者的體驗 |
| 代表工具 | Lighthouse(開發者工具內建) | Vercel Speed Insights、Search Console 的 Core Web Vitals 報告 |
| 優點 | 隨時能跑,馬上看到問題在哪 | 反映真實的手機、網路狀況 |
| 缺點 | 只是模擬,不等於使用者的感受 | 要有足夠的流量才有數據 |
Lighthouse 拿 100 分,不代表使用者覺得快。

【圖 7|Biashelf 首頁的 PageSpeed Insights 結果】
這些建議很適合交給 AI 判斷:
以下是 PageSpeed Insights 的報告。請說明每一項建議影響的是哪個指標、
對真實使用者的影響大不大,依優先順序排列。先不要修改。
兩個提醒:AI 很容易為了衝分數,去改一些對使用者沒什麼差別的地方,所以要它先說明「影響哪個指標」;改完之後,再跑一次比對前後。
Lighthouse 和 PageSpeed Insights 都要自己去查。Vercel Speed Insights 則是在網站裡放一段程式碼,每個真實使用者打開頁面時,都會回報這次的體驗,而且可以分頁面看。
設定流程:
請安裝 Vercel Speed Insights:安裝 @vercel/speed-insights,
並在 root layout 加入 <SpeedInsights />。
這次允許修改 root layout,但只加這一處,其他內容不要動。

【圖 8|Vercel Speed Insights 畫面】
免費版只提供一個總分(Real Experience Score),要看到每個指標的細節(哪一頁特別慢之類的),需要另外付費升級。
Speed Insights 和下一節的 GA4,都是真實使用者的數據,也都能分頁面看。差別在於:Speed Insights 量的是「快不快」,GA4 量的是「做了什麼」。
| GA4(Google Analytics 4) | Vercel Web Analytics | |
|---|---|---|
| 設定 | 要建立帳戶、取得評估 ID | 在 Vercel 開啟,裝一個套件 |
| 識別訪客的方式 | 常會用到 Cookie 等瀏覽器識別資訊,要注意隱私設定 | 主打輕量、不追蹤個人的流量分析 |
| 功能 | 完整:自訂事件、轉換漏斗、串接 Search Console 和廣告 | 簡單:流量、熱門頁面、來源 |
| 費用 | 免費 | 有免費額度 |
這次選 GA4,因為它是業界最常見的工具,學會了在別的專案也用得上;而且可以跟 Search Console 串在一起,看到使用者是搜尋什麼進來的。只想看流量的話,Vercel Web Analytics 更簡單。
GA4 不用驗證網域。它的評估 ID 像一個「收件地址」,網站把資料寄到這個地址,你登入就收得到;Search Console 給你看的是 Google 掌握的資料,所以才需要證明網域是你的。
Google Analytics 這邊:
你的網域,依序設定完 DNS 後,會拿到一組 G- 開頭的評估 ID。畫面上那段「手動安裝代碼」可以跳過,交給 AI 用 Next.js 的方式載入。
【圖 9|GA4 後台建立新帳戶】

【圖 10|輸入完基本資料,選擇要追蹤的平台】

【圖 11|可以看得到追蹤碼(G-XXXXXXXXXX),安裝完、部署完,可以測試】
網站這邊,交由Codex 處理:
請幫 Biashelf 加上 Google Analytics 4,評估 ID 是 G-XXXXXXXXXX。
1. 使用 @next/third-parties 的 GoogleAnalytics 元件,不要直接貼 gtag 的 <script>
2. 只在正式環境載入:判斷 VERCEL_ENV === "production",Preview 與本機都不要載入
3. 這次允許修改 root layout,但只加這一處,其他內容不要動
兩個細節:
部署後打開正式網站點幾頁,到 GA4 的「即時」報表,應該會看到自己。

【圖 12|GA4 即時報表】
只看瀏覽量,只能知道「有人來」。真正有用的,是使用者做了什麼:
| 事件 | 回答什麼問題 | 怎麼來的 |
|---|---|---|
search |
大家都在搜尋哪些團、哪些專輯? | 加強型評估自動記錄 |
view_album |
哪些專輯最多人看? | 自訂 |
add_favorite |
收藏功能有人用嗎? | 自訂 |
能用 GA4 建議的事件名稱就先用,沒有合適的再自己命名。自訂事件可以等基本追蹤確認正常之後再加,一次只做一件事比較好除錯。
不要把 email、姓名這類能直接認出一個人的資料送進 GA4,這是 GA4 的規範明文禁止的。
電商最常用的是轉換漏斗:追蹤「加入購物車 → 開始結帳 → 完成購買」,看出多少人在哪一步離開。
Vercel 本身就有執行紀錄(Runtime Logs),但它跟 Sentry 擅長的地方不一樣:
| Vercel Runtime Logs | Sentry | |
|---|---|---|
| 抓得到什麼 | 伺服器端的錯誤 | 伺服器端,加上使用者瀏覽器裡的錯誤 |
| 保留多久 | Pro 方案 1 天(依方案不同) | 較長 |
| 出事時 | 要自己去翻 | 主動通知;上傳 source map 後,可以對應到原始程式碼的哪一行 |
| 費用 | 包含在方案裡 | 有免費方案,額度以官網為準 |
關鍵在第一列:使用者瀏覽器裡發生的錯誤,伺服器根本不知道。 某支手機上按鈕點了沒反應,Vercel 的 log 裡什麼都不會有。
先在 Sentry 建立專案(平台選 Next.js),取得 DSN。DSN 決定錯誤要送到哪個專案,網站不需要知道你的 Sentry 帳號密碼。
接著在專案根目錄執行官方的安裝精靈。它會打開瀏覽器讓你登入、選擇專案,再自動安裝套件、建立設定檔:
npx @sentry/wizard@latest -i nextjs
精靈開始前如果提醒「有尚未 commit 的修改」,先停下來把目前的修改 commit 起來。精靈會修改好幾個檔案,跟其他修改混在一起,之後就很難分辨、也很難單獨退回。
精靈用的是官方範本,比 AI 自己寫的設定可靠;但它不知道 Biashelf 的需求,所以裝完再交給 AI 檢查(下一節的設定)。
裝的過程中會碰到兩把鑰匙:
| 用途 | 可以交給 AI 嗎? | |
|---|---|---|
| DSN | 告訴網站錯誤要送到哪裡,本來就會出現在前端 | 可以 |
| auth token | 部署時上傳 source map 的權限 | 不要貼進對話或 commit,放在 Vercel 的環境變數 |
source map 是「壓縮後的程式碼,對應到原始程式碼哪一行」的對照表。正式版的程式碼在打包時會被壓縮,沒有它,錯誤只會顯示「page-3f8a2c.js 第 1 行第 28471 個字」;有了它,才會顯示「AlbumDetails.tsx 第 117 行」。沒有 auth token,Sentry 一樣收得到錯誤,只是比較難看懂。
auth token 不能加 NEXT_PUBLIC_;GitHub Actions 的 CI 也不需要它,沒有 token 只是略過上傳。
Sentry 有很多功能可以開關,精靈過程中會問一部分,其他要裝完後自己調整:
| 設定 | 做什麼 | Biashelf 的選擇 | 什麼情況會想開 |
|---|---|---|---|
| 啟用範圍 | 哪些地方會送出錯誤 | 只在 Vercel 上的部署啟用 | — |
| 環境標記 | 區分錯誤來自 Preview 還是正式版 | 依 Vercel 的環境名稱標記 | — |
資料收集範圍(dataCollection) |
錯誤裡要不要附上 IP、Cookie、請求內容等資訊 | 減少收集 | 需要追蹤特定使用者的問題,而且隱私說明已經寫清楚 |
| 效能追蹤(Tracing) | 記錄每次請求花了多少時間 | 關閉 | 想找出哪個頁面、哪支 API 特別慢 |
| Session Replay | 錄下使用者出錯前的操作畫面 | 關閉 | 錯誤很難重現,需要看使用者做了什麼;要注意隱私與額度 |
| Logs | 把程式的紀錄訊息也送到 Sentry | 關閉 | 想把錯誤和前後的紀錄放在一起看 |
| Tunnel | 瀏覽器的錯誤先送到自己的伺服器再轉給 Sentry,避開廣告阻擋器 | 關閉 | 發現前端錯誤回報明顯偏少;代價是增加 Vercel 的用量 |
| 測試頁面 | 精靈建立一頁可以故意觸發錯誤的頁面 | 開啟,確認接通後刪除 | — |
前三項最容易被忽略,而且精靈的預設值不一定符合需求:
dataCollection 設定把這些關掉。舊版用的是 sendDefaultPii,新版已經改成 dataCollection。後面三項功能都很實用,但都會消耗免費額度。先從錯誤監控開始,需要時再打開。
例如,交給 AI 的檢查指令範例:
我剛用 Sentry 安裝精靈完成設定,請檢查並調整,先列出要改的地方,等我確認:
1. 只在 Vercel 上的部署啟用:有 VERCEL_ENV(前端用 NEXT_PUBLIC_VERCEL_ENV)才啟用,
本機開發、本機 npm run start、GitHub Actions 的 CI 都不要送錯誤
2. 環境標記:environment 使用同一個值,區分 Preview 與正式版
3. 確認 dataCollection 已關閉使用者資訊、Cookie、HTTP 內容、網址參數與資料庫查詢內容
4. 確認效能追蹤、Session Replay、Logs、Tunnel 都沒有開啟
5. 確認 auth token 沒有被 commit,也沒有 NEXT_PUBLIC_ 前綴
設定好之後,部署到 Preview,打開精靈建立的測試頁面觸發一次錯誤。錯誤會出現在 Sentry 的 Issues 列表:同一種錯誤會合併成一筆,顯示發生幾次、影響幾個使用者、發生在哪一行程式碼。

【圖 13|可以從 Sentry 幫專案創建的範例頁面送出測試錯誤】
通知規則可以自己設定,例如「正式環境出現新的錯誤,就寄信」,Preview 的測試錯誤就不會吵到你。
確認 Sentry 收得到、環境標記正確、正式通知沒有被觸發,才算真的裝好。沒有故意弄壞過,就不知道它會不會響。

【圖 14|四個工具的設定流程:哪些自己做、哪些交給 AI】
四條流程幾乎都是同一個模式:自己在第三方後台設定 → 交給 AI 改程式 → 部署 → 自己確認。
GA4、Speed Insights、Sentry 都要改程式,改完要重新部署才會生效;Search Console 的設定在網站外完成,但它要讀的 sitemap,一樣要先部署上去。至於 Preview 或開發時的數據是否影響,建議要設定一下,可以靠後台篩選和環境標記區分。
公開的識別碼可以交給 AI,秘密和第三方帳號自己處理,跟 Day 26 的分界一樣。GA4 評估 ID、Sentry DSN 可以直接給 AI;Sentry auth token 不要貼進對話或程式碼,放在 Vercel 的環境變數;Search Console 驗證、GA4 建立帳戶這類要登入第三方後台的步驟,也都自己來。
也有現成的 SEO audit skill(例如 seo-audit),不過這類 skill 多半偏向行銷面:關鍵字、競爭對手、內容缺口。技術面的檢查,用 2.3 那種自己寫的指令在提示詞中提及。
| AI 很容易做的事 | 後果 |
|---|---|
| 重要內容要等瀏覽器執行 JavaScript 才出現 | Google 要多花一步處理,收錄比較不穩定;LINE、Facebook 抓預覽時不會執行 JavaScript,卡片可能是空的 |
| 測試時加了「不收錄」,忘了拿掉 | 整個網站從 Google 消失 |
| 把 email 送進 GA4 或 Sentry | 個人資料外流到第三方 |
把 Sentry 的 auth token 加上 NEXT_PUBLIC_ |
秘密被打包進前端 |
| 測試 | 預期結果 |
|---|---|
| 讀取專輯頁的原始 HTML | 包含分享圖,以及所有版本的名稱與配置 |
| 把專輯網址貼到 LINE | 出現專輯封面與名稱的預覽卡片 |
打開正式網站的 /sitemap.xml、/robots.txt |
網址都是正式網域;robots 只排除 /api/ |
打開 /albums?q=aespa |
頁面設為不收錄,canonical 指向 /albums |
| Search Console 網址檢查 | 專輯頁可以被收錄 |
| PageSpeed Insights | 記錄專輯頁的結果,挑出優先處理的項目 |
| GA4 即時報表 | 正式版的瀏覽與搜尋事件出現;Preview 不會 |
| 在 Preview 丟出錯誤 | Sentry 收到並標記為 Preview,不觸發正式通知 |
| 產品 | SEO 的重點 | 最該追蹤的事件 | 最怕不知道的錯誤 |
|---|---|---|---|
| 電商 | 商品頁 | 加入購物車、結帳 | 付款按鈕沒反應 |
| 部落格 | 每篇文章 | 閱讀完成、訂閱 | 文章頁打不開 |
| SaaS | 介紹頁與文件 | 註冊、開始試用 | 註冊流程卡住 |
今天的新觀念:
| 觀念 | 一句話 |
|---|---|
| SEO | 讓搜尋引擎爬得到、看得懂 |
| robots.txt vs noindex | 一個管要不要來爬,一個管要不要收錄 |
| AEO | 讓 AI 更容易引用你的內容 |
| Core Web Vitals | 用 LCP、INP、CLS 衡量使用者覺得快不快 |
| 實驗室 vs 真實數據 | 模擬一次的分數,不等於使用者的感受 |
| 錯誤監控 | 網站壞掉時,主動通知我 |
今天的三個發現,其實都在講同一件事:畫面上版本切換正常,爬蟲卻只看得到第一個;Lighthouse 分數很高,不代表使用者覺得快;裝了 Sentry,不代表警報真的會響。
我眼前看起來正常,不等於外面的世界看到的也正常。 今天打出的信號彈,一邊是讓別人看見 Biashelf,一邊是讓我看見使用者真正遇到的事。
不過,每多接一個服務,有時候就多一份帳單:網域、Vercel、Supabase、寄信、監控……網站能不能長久營運,最後還是要回到錢。明天,我們來算一算這艘船的開銷,並且嘗試著透過這個網站來獲得一些收入。
我們明天見。